<?php

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 * Description of BApp
 * This class define relationship between authItem and operations
 *
 * @author jianfeng
 */
abstract class BServiceApp extends BNode implements BIServiceApp {
    /*
     * operation can be function inside this class
     * or function from other classes
     * this is collection of operations used in this app
     */

    const VISIABLETOSERVICE = "visiable";
    const INVISIABLETOSERVICE = 'invisiable';
    const SERVICETYPESCOPETYPE = "servicetype_scope";
    const SERVICESCOPETYPE = "service_scope";
    const SERVICECATEGORYSCOPETYPE = "servicecategory_scope";
    const INCLUDEAPPLYRULE = 'include';
    const EXCLUDEAPPLYRULE = 'exclude';
    const REGISTERAPPFLAG = 'register_app';

    //   const BServiceAppForService = 'application.application.BserviceAppForService';   

    protected $appData = array();
    private $authManager = null;
    protected $dbAdapter = null;
    protected $uiManager = null;
    protected $appname = null;
    protected $id = null;
    protected $description = null;
    protected $showAble = null;
    protected $activeService = null;
    protected $appConfig = null;
    public $basePath = null;
    protected $classPath = null;
    public $configPath = null;
    protected $dataPath = null;
    public $uiPath = null;
    protected $operations = null;
    protected $isRegisterred = false;
    //protected $operationRequests = null;
    protected $appMeta = null;
    protected $appRoles = null;
    protected $appOperations = null;
    protected $operationsWithAccessList = array();

    /**
     *
     * @return BAuthManager 
     */
    public function __construct($activeService = null, $isRegistered = TRUE) {
        $this->init();

        $id = $this->getDbAdapter()->getServiceAppId(get_class($this));

        if ($id == null) {
            $id = $this->register();
        }

        $this->load($id, $activeService);

        /*
          if ($isRegistered) {
          if ($this->id == null) {
          $id = $this->getDbAdapter()->getServiceAppId(get_class($this));
          $this->load($id, $activeService);
          }
          }
         * 
         */
    }

    protected function init() {
        $config = $this->getAppConfig();
        $config->init();

        if ($this->uiManager == NULL)
            $this->uiManager = $this->getUIManager();
    }

    /**
     * 
     * @return BAuthManager
     */
    public function getAuthManager() {
        if ($this->authManager == null) {
            $this->authManager = new BAuthManager($this);
        }
        return $this->authManager;
    }

    /**
     *
     * @return BUINode
     */
    public function getUIManager($style = NULL) {
        if ($this->uiManager == null) {
            $this->uiManager = new BUIServiceApp($this);
            //  $this->uiManager->setActiveService($this->getActiveService());
        }

        $this->uiManager->setActiveService($this->getActiveService());
        //$this->uiManager->setActiveServiceApp($this);

        return $this->uiManager;
    }

    public function getConfigManager() {
        return $this->getAppConfig();
    }

    /**
     *
     * @return BDbAdapter 
     */
    public function getDbAdapter() {
        if ($this->dbAdapter == null) {
            $this->dbAdapter = new BDbAdapter($this);
        }
        return $this->dbAdapter;
    }

    public function getAppConfig() {
        if ($this->appConfig == NULL) {
            $this->appConfig = new BServiceAppConfig($this);
        }
        return $this->appConfig;
    }

    protected function setUIManager($uiManager) {
        $this->uiManager = $uiManager;
    }

    protected function setDbAdapter($dbAdapter) {
        $this->dbAdapter = $dbAdapter;
    }

    protected function setAuthManager($authManager) {
        $this->authManager = $authManager;
    }

    /**
     *
     * @param type $operationName
     * @param type $config 
     */
    public function doOperation($operationName, $config = NULL) {

        //load operation from config.php meta file and set startScreen or startScreenFlow
        if (isset($this->appOperations[$operationName]) && Blyn::app()->getActiveOperation() != NULL) {

            $operation = Blyn::app()->getActiveOperation();

            if (Blyn::app()->getActiveOperation()->getName() != $operationName)
                $operation = $this->getOperationByName($operationName);

            $operationSetting = $this->appOperations[$operationName];

            if (isset($operationSetting['startScreen']))
                $operation->getUIManager()->setStartScreen($operationSetting['startScreen']);

            if (isset($operationSetting['startScreenFlow']))
                $operation->getUIManager()->setStartScreenFlow($operationSetting['startScreenFlow']);
        }
    }

    /**
     *
     * @param type $appId
     * @param type $activeService
     * @return BServiceApp
     */
    public static function getInstanceById($appId, $activeService = null) {
        if ($appId != null) {

            $db = new BDbAdapter();

            $appClass = $db->getServiceAppClass($appId);

            $app = new $appClass($activeService);

            //$app->load($appId, $activeService);
            //Blyn::app()->addServiceAppIntoPool($app);

            return $app;
        }
        return false;
    }

    public function load($appId, $activeService = null) {
        if ($appId != null) {
            $mApp = BMApplication::model()->findByPk($appId);

            $this->appname = $mApp->appname;
            $this->description = $mApp->description;
            $this->showAble = $mApp->showable;
            $this->appData = unserialize($mApp->data);
            $this->id = $appId;

            //register here will check change of config file and update
            //config into database
            $this->register();

            if ($activeService !== NULL)
                $this->activeService = $activeService;

            //get operation request when load serviceApp
            //$this->operationRequests = $this->getOperationRequests();
        }
    }

    public function setActiveService($service) {
        $this->activeService = $service;
    }

    public function getActiveService() {
        if ($this->activeService == NULL)
            $this->activeService = Blyn::app()->getActiveService();
        return $this->activeService;
    }

    public function addToService($service = null) {

        if (Blyn::app()->getActiveService() instanceof BService)
            $serviceId = Blyn::app()->getActiveService()->getId();

        if ($service != null) {
            if (is_numeric($service))
                $serviceId = $service;
            if ($service instanceof BService)
                $serviceId = $service->getId();
        }

        $db = $this->getDbAdapter();
        $addResult = $db->addServiceApp($serviceId, $this->getId());
        if ($addResult != NULL)
            return $this->addRolesToService($serviceId);
    }

    protected function updateAppData($data) {

        if ($this->appData == NULL)
            $this->appData = array();

        $this->appData = array_merge($this->appData, $data);

        $db = $this->getDbAdapter();
        $db->updateAppData($this->appData);
    }

    public function removeFromService($service = null) {
        
    }

    public function register() {

        $id = $this->registerAppMeta();
        $this->registerRoles($id);
        $this->registerOperations($id);

        return $id;
    }

    public function unRegister() {
        
    }

    public function addRolesToService($service) {
        return TRUE;
    }

    public function getId() {
        if ($this->id == null) {
            $db = $this->getDbAdapter();
            $this->id = $db->getServiceAppId(get_class($this));

            /*
              if ($this->id == null) {
              $this->register();
              }
             * 
             */
        }
        return $this->id;
    }

    public function getName() {
        return $this->appname;
    }

    public function isShowAble() {

        return $this->showAble;
    }

    /**
     * This function return visible property of serviceApp,scenario of visible 
     * list as below:
     * 1. if visible to specific service, then should specify service
     * 2. is visible to specific service type
     * 3. if visible to specific categories
     * @param type $service
     * @param type $serviceType
     * @param type $serviceCategory
     * @return type 
     */
    public function isVisible($service = null, $serviceType = null, $serviceCategory = null) {

        $serviceTypeId = null;
        $serviceId = null;
        $serviceCategoryId = null;
        $serviceCategoryIds = array();

        if ($service instanceof BService) {
            $serviceId = $service->getId();
            $categorys = $service->getServiceCategorys();
            foreach ($categorys as $category) {
                array_push($serviceCategoryIds, $category->_id);
            };
            $serviceTypeId = $this->getDbAdapter()->getServiceTypeId($service->getServiceType());
        }

        if ($serviceType != null)
            $serviceTypeId = $this->getDbAdapter()->getServiceTypeId($serviceType);

        if ($serviceCategory != null) {
            if (is_numeric($serviceCategory))
                $serviceCategoryId = $serviceCategory;
            if (is_string($serviceCategory))
                $serviceCategoryId = $this->getDbAdapter()->getServiceCategoryId($serviceCategory);
            array_push($serviceCategoryIds, $serviceCategoryId);
        }

        return $this->getDbAdapter()->isVisible($serviceId, $serviceTypeId, $serviceCategoryIds);
    }

    protected function isRegistered() {
        $id = $this->getDbAdapter()->getServiceAppId(get_class($this));

        if ($id == null)
            return false;

        return true;
    }

    protected function isNeedUpdateApp() {
        return false;
    }

    protected function registerRoles($appId) {
        $metaRoles = $this->getConfigManager()->getConfig('appRoles');

        $appRoles = isset($this->appData['appRoles']) ? $this->appData['appRoles'] : array();

        if (!BUtil::arraysEqual($metaRoles, $appRoles)) {
            $roleTemplate = new BBasicRolesTemplate($appId);
            $roleTemplate->importRolesByMeta($metaRoles);
            $roleTemplate->register();
            $this->updateAppData(array('appRoles' => $metaRoles));
        }
        $this->appRoles = $metaRoles;
    }

    protected function registerOperations($appId) {

        $db = $this->getDbAdapter();

        //$operations1 = $this->registerAppOperations();
        $metaOperations = $this->getConfigManager()->getConfig('appOperations');
        $appOperations = isset($this->appData['appOperations']) ? $this->appData['appOperations'] : array();

        if (!BUtil::arraysEqual($metaOperations, $appOperations)) {
            $db->registerOperations($appId, $metaOperations);
            $this->updateAppData(array('appOperations' => $metaOperations));
        }

        $this->appOperations = $metaOperations;
    }

    protected function registerAppMeta() {
        $id = $this->id;

        $metaAppMeta = $this->getConfigManager()->getConfig('appMeta');
        $dbAppMeta = isset($this->appData['appMeta']) ? $this->appData['appMeta'] : null;

        if (!isset($metaAppMeta['appClass']))
            $metaAppMeta['appClass'] = get_class($this);

        if ($id == null || $dbAppMeta == NULL || !BUtil::arraysEqual($metaAppMeta, $dbAppMeta)) {
            $db = $this->getDbAdapter();

            $this->updateAppData(array('appMeta' => $metaAppMeta));

            $id = $db->registerServiceApp($metaAppMeta);
        }

        $this->appMeta = $metaAppMeta;

        return $id;
    }

    public function getOperationByName($name) {
        $oppId = $this->getDbAdapter()->getOperationId($this->getId(), $name);
        $opp = new BOperation($oppId);
        $opp->setActiveService($this->getActiveService());
        return $opp;
    }

    public function getOperationById($id) {
        $opp = new BOperation($id);
        $opp->setActiveService($this->getActiveService());
        return $opp;
    }

    public function checkAccess($operationName, $roles = NULL) {

        $temArray = explode("::", $operationName);
        $operationName = $temArray[1];
        //getCurrentUser's role
        $currentUINode = Blyn::app()->getCurrentNode()->getUIManager();

        $config = $this->getAppConfig();
        $checkList = $config->getConfig('checkList');

        $checkResult = $currentUINode->getTransitVariable('CheckResult');

        if ($checkResult == NULL) {
            $checkResult = new BCheckAccessResult();
            $result = $this->getAuthManager()->checkAccess($operationName, $checkList);
            $checkResult->setResult($result);
            $currentUINode->addTransitVariable('CheckResult', $checkResult);
        }

        if ($checkResult->getResult() == FALSE) {
            $checkResult->showResult();
            yii::app()->end();
        }
    }

    /**
     * 
     * @param type $serviceId
     * @param type $userId
     * @param type $roleType
     * @param type $roles
     * @return array by default, return current user with active service and activerserviceApp's operations
     * can also specifi roletype or roles for allow operations
     */
    public function getUserOperations($serviceId = NULL, $userId = NULL, $roleType = array(), $roles = array()) {

        //this can judge serviceApp from workteam or from served team        
        if (isset($_REQUEST['rtp'])) {
            $rtp = $_REQUEST['rtp'];

            if ($rtp == BService::WORKTEAMTYPE)
                $roleType = array(BBasicRolesTemplate::SERVICE_ADMIN_ROLE,
                    BBasicRolesTemplate::SERVEDTEAM_ADMIN_ROLE,
                    BBasicRolesTemplate::WORKTEAM_MEMBER_ROLE,
                    BBasicRolesTemplate::WORKTEAM_ADMIN_ROLE);
            if ($rtp == BService::SERVEDTEAMTYPE)
                $roleType = array(BBasicRolesTemplate::SERVEDTEAM_MEMBER_ROLE);
        }

        $allowOperations = array();

        if ($userId == NULL)
            $userId = Blyn::app()->getCurrentUser()->getId();

        if ($serviceId == NULL)
            $serviceId = Blyn::app()->getActiveService()->getId();

        //get all roles of user, include parent cirles roles and parent/child roles according type of role
        if ($roles == NULL)
            $roles = $this->getAuthManager()->getUserRoles($userId, $serviceId);

        if ($roleType != NULL) {
            $tmpRoles = array();
            foreach ($roles as $role) {
                if (in_array($role->getAuthType(), $roleType))
                    array_push($tmpRoles, $role);
            }

            $roles = $tmpRoles;
        }

        $operationsList = $this->getOperationsWithAccessList();

        foreach ($operationsList as $opName => $opData) {
            $allowAccessRoleIds = $opData['allowAccess'];

            $denyAccessRoleIds = isset($opData['denyAccess']) ? $opData['denyAccess'] : array();
            //if no senderpermits, set senderPermits as allaccess permits
            $senderPermitIds = isset($opData['senderPermit']) ? $opData['senderPermit'] : $allowAccessRoleIds;

            //add login member allowed operation into list
            if (in_array(BBasicRolesTemplate::LOGIN_MEMBER_ROLE_ID, $allowAccessRoleIds)) {
                array_push($allowOperations, $opName);
                continue;
            }

            foreach ($roles as $role) {
                $roleId = $role->getId();
                if (in_array($roleId, $denyAccessRoleIds))
                    break;
                if (in_array($roleId, $allowAccessRoleIds) && in_array($roleId, $senderPermitIds)) {
                    array_push($allowOperations, $opName);
                }
            }
        }

        $operations = array();

        foreach ($allowOperations as $operationName) {
            array_push($operations, $this->getOperationByName($operationName));
        }

        return $operations;
    }

    /**
     * return an operation list with accesslist
     */
    protected function getOperationsWithAccessList() {

        if ($this->operationsWithAccessList == NULL) {

            $operations = $this->appOperations;
            $operationWithRoleIdList = array();

            foreach ($operations as $key => $operation) {
                $opName = $key;
                if (!isset($operation['permit'])) {
                    //set default role for any operation is service member, means all service member can access operation
                    $operation['permit'] = array('allowAccess' => BBasicRolesTemplate::SERVICE_MEMBER_ROLE);
                }

                $allowAccessRoles = $operation['permit']['allowAccess'];
                $operationWithRoleIdList[$opName] = array('allowAccess' => $this->getAuthManager()->getRoleIdList($allowAccessRoles));

                if (isset($operation['permit']['denyAccess'])) {
                    $allowAccessRoles = $operation['permit']['denyAccess'];
                    $operationWithRoleIdList[$opName]['denyAccess'] = $this->getAuthManager()->getRoleIdList($allowAccessRoles);
                }

                if (isset($operation['operationContract'])) {
                    $contractClass = $operation['operationContract']['class'];
                    $opContract = new $contractClass;

                    $myRole = $operation['operationContract']['myRole'];

                    $permits = FALSE;

                    if (strpos($myRole, "sender") !== FALSE) {
                        $permits = $opContract->getOperationPermits($opName, 'senderPermit');
                    }

                    if ($permits !== FALSE) {
                        $senderPermitIdList = $this->getAuthManager()->getRoleIdList($permits);
                        $operationWithRoleIdList[$opName]['senderPermit'] = $this->getAuthManager()->getRoleIdList($senderPermitIdList);
                    }
                }
            }

            $this->operationsWithAccessList = $operationWithRoleIdList;
        }

        return $this->operationsWithAccessList;
    }

    public function getRegisterAppMeta() {
        $config = $this->getConfigManager();
        $appMeta = $config->getConfig('appMeta');

        return $appMeta;
    }

    public function getOperationMeta($operation) {
        $config = $this->getConfigManager();
        $operationsMeta = $config->getConfig('appOperations');

        return $operationsMeta[$operation];
    }

    public function getActiveOperation() {
        return NULL;
    }

    public function getActiveServiceApp() {
        return $this;        
    }

    public function beforeRenderServiceApplication($data = array()) {
        
    }

    public function afterRenderServiceApplication($data = array()) {
        
    }
    
    public function beforeRenderOperation($operation,$data=array()){
        
    }
    
    public function afterRenderOperation($operation,$data=array()){
        
    }

}

?>
